﻿module Tutorial

open System
open System.Collections.Generic
open System.Linq
open System.Text.RegularExpressions
open Microsoft.FSharp.Collections
open Microsoft.Xna.Framework
open Microsoft.Xna.Framework.Audio
open Microsoft.Xna.Framework.Content
open Microsoft.Xna.Framework.GamerServices
open Microsoft.Xna.Framework.Graphics
open Microsoft.Xna.Framework.Input
open Microsoft.Xna.Framework.Media
open EntitiesAndGameState
open Component
open UnfoldMonad
open Services
open Utilities
open AudioComponent
open TutorialStr

let check_defeat () = 
  unfold{
    do! wait_while_
          (unfold{
              let! state = use_state (fun s -> unfold {return s})              
              if state.EarthLife <= 0 then return false
              else return true
          })
  }

let random =new Random()
let rec generate_enemies n =    
    unfold {
      if n > 0 then
        let level = 5
        let time_to_next_spawn = 
          ((-Math.Atan(float level / 3.0 - 1.0) + float MathHelper.PiOver2) + (random.NextDouble()) * (-Math.Atan(float level / 3.0 - 1.0) + float MathHelper.PiOver2)) |> float32

        do! wait time_to_next_spawn

        let difficulty = ref ((float32) level / 15.0f)

        if !difficulty > 1.0f then difficulty := 1.0f
        else difficulty := 0.0f

        let start_dest = UtilsWAG.generateStartDestinationPoints(!difficulty,random)
        let enemie_type_to_add = enum<GarbageType>(random.Next(0, 6))
        let enemie = new ShipToAdd(start_dest.[0],Vector3(12.5f,0.0f,8.0f),false,1.0f,enemie_type_to_add,true,true)

        do! add_enemie enemie
        return! generate_enemies (n - 1)
    }

let manage_earth_life = 
  unfold {     
    do! forever_ 
          (unfold {
            do! wait 5.0f
            Background.EarthLife <- Math.Min(Background.EarthLife + 1,Background.MAX_EARTH_LIFE)
          })
    }  

let is_ship_stopped ship_index = 
  unfold { 
              let! garbage = get_garbage ship_index
              if  garbage.geometry.Velocity.X < 0.2f && 
                  garbage.geometry.Velocity.Y < 0.2f && 
                  garbage.geometry.Velocity.Z < 0.2f then
                  do! use_garbage ship_index (fun g -> unfold { 
                                                          g.engines.[0].Throttle <- 0.0f 
                                                          g.engines.[1].Throttle <- 0.0f 
                                                        })
                  return false
              else
                return true
  }

let sum_unfold =
  (fun (a:int,r1,rt1,gt1) (b:int,r2,rt2,gt2) -> (a + b))

let check_if_is_one_yellow_collide_with_the_wrong_trash_bin (strings:TutorialStrings) =
  unfold{
    do! forever_
          (unfold{
            let! enemies_to_remove = get_enemies_to_remove

            let value =
              let rec resolve num =            
                  if num < 0 then 
                    []
                  elif  enemies_to_remove.[num].remove_it && 
                        (enemies_to_remove.[num].garbage_type = GarbageType.EnemyYellowBook ||
                         enemies_to_remove.[num].garbage_type = GarbageType.EnemyYellowPaper) then                    
                    num :: (resolve (num - 1 ))
                  else
                    resolve (num - 1)
              resolve (enemies_to_remove.Length - 1)

            if  value <> [] then                       
              let rec create num = 
                match num with
                [] -> unfold { return () }
                |x::xs ->
                    unfold {
                      let paper_ship = new ShipToAdd(Vector3(5.0f,0.0f,0.1f),Vector3(0.1f,0.0f,0.1f),true, 0.0f,GarbageType.EnemyYellowBook,true,true)
                      do! set_enemy_to_remove x false
                      do! add_enemie paper_ship               
                      return! create xs
                    }   
              do! create value  
              do! wait_and_print_without_time 2.0f strings.Wrong (Vector2(50.0f, 87.5f)) Color.White (Vector2(80.0f, 25.0f))              
            })
  }

let check_if_is_one_blue_collide_with_the_wrong_trash_bin (strings:TutorialStrings)=
  unfold{
    do! forever_
          (unfold{
            let! enemies_to_remove = get_enemies_to_remove

            let value =
              let rec resolve num =            
                  if num < 0 then 
                    []
                  elif  enemies_to_remove.[num].remove_it && 
                        (enemies_to_remove.[num].garbage_type = GarbageType.EnemyBlueBottle ||
                         enemies_to_remove.[num].garbage_type = GarbageType.EnemyBlueTin) then                    
                    num :: (resolve (num - 1 ))
                  else
                    resolve (num - 1)
              resolve (enemies_to_remove.Length - 1)

            if  value <> [] then                       
              let rec create num = 
                match num with
                [] -> unfold { return () }
                |x::xs ->
                    unfold {
                      let plastic_ship = new ShipToAdd(Vector3(-5.0f,0.0f,0.1f),Vector3(0.1f,0.0f,0.1f),true, 0.0f,GarbageType.EnemyBlueBottle,true,true)
                      do! set_enemy_to_remove x false
                      do! add_enemie plastic_ship               
                      return! create xs
                    }   
              do! create value  
              do! wait_and_print_without_time 2.0f strings.Wrong (Vector2(50.0f, 87.5f)) Color.White (Vector2(80.0f, 25.0f))              
            })
  }
let all_enemies_defeated = 
  unfold{
    let! enemies_to_remove = get_enemies_to_remove
    let! garbages = get_garbages
    let value =
      let rec resolve num =            
          if num < 0 then 
            0
          elif  enemies_to_remove.[num].remove_it && 
              ( enemies_to_remove.[num].garbage_type = GarbageType.EnemyBlueBottle ||
                enemies_to_remove.[num].garbage_type = GarbageType.EnemyBlueTin ||
                enemies_to_remove.[num].garbage_type = GarbageType.EnemyGreenMeat ||
                enemies_to_remove.[num].garbage_type = GarbageType.EnemyGreenSandwich ||
                enemies_to_remove.[num].garbage_type = GarbageType.EnemyYellowBook ||
                enemies_to_remove.[num].garbage_type = GarbageType.EnemyYellowPaper) then
            1 + (resolve (num - 1 ))
          else
            resolve (num - 1)
      resolve (enemies_to_remove.Length - 1)
    return garbages.Count = 0 && value = 0
  }
let check_if_is_one_green_collide_with_the_wrong_trash_bin (strings:TutorialStrings) =
  unfold{
    do! forever_
          (unfold{
            let! enemies_to_remove = get_enemies_to_remove

            let value =
              let rec resolve num =            
                  if num < 0 then 
                    []
                  elif  enemies_to_remove.[num].remove_it && 
                        (enemies_to_remove.[num].garbage_type = GarbageType.EnemyGreenMeat ||
                         enemies_to_remove.[num].garbage_type = GarbageType.EnemyGreenSandwich) then                    
                    num :: (resolve (num - 1 ))
                  else
                    resolve (num - 1)
              resolve (enemies_to_remove.Length - 1)

            if  value <> [] then                       
              let rec create num = 
                match num with
                [] -> unfold { return () }
                |x::xs ->
                    unfold {
                      let meat_ship = new ShipToAdd(Vector3(0.1f,0.0f,0.1f),Vector3(0.1f,0.0f,0.1f),true, 0.0f,GarbageType.EnemyGreenMeat,true,true)
                      do! set_enemy_to_remove x false
                      do! add_enemie meat_ship               
                      return! create xs
                    }   
              do! create value  
              do! wait_and_print_without_time 2.0f strings.Wrong (Vector2(50.0f, 87.5f)) Color.White (Vector2(80.0f, 25.0f))              
            })
  }

let tutorial_logic (audioManager:IAudioManager) (strings:TutorialStrings) = 
  unfold{   
    do! disable_manhole
    
    let meat_ship = new ShipToAdd(Vector3(0.0f, 0.0f, -6.8f),Vector3(0.1f,0.0f,0.1f),false, 2.0f,GarbageType.EnemyGreenSandwich,true,true)
    let meat_ship_index = 0
    do! add_enemie meat_ship

    do! wait 2.0f    
    do! set_render_text strings.One (Vector2(50.0f, 87.5f)) Color.White (Vector2(80.0f, 25.0f))    
    do! wait_while_ (is_ship_stopped meat_ship_index)

    do! wait 2.0f    
    do! set_render_texts_with_tap [strings.Two] (Vector2(50.0f, 87.5f)) Color.White (Vector2(80.0f, 25.0f))
    do! remove_top_render_text

    let plastic_ship = new ShipToAdd(Vector3(-5.0f, -0.2f, 0.0f),Vector3(0.0f, -0.2f, 0.0f),false, 0.0f,GarbageType.EnemyBlueBottle,true,true)
    let plastic_ship_index = 1
    do! add_enemie plastic_ship

    do! wait 1.0f
    let paper_ship = new ShipToAdd(Vector3(5.0f, -0.2f, 0.0f),Vector3(0.0f, -0.2f, 0.0f),false, 0.0f,GarbageType.EnemyYellowBook,true,true)
    let paper_ship_index = 2
    do! add_enemie paper_ship

    do! wait 1.5f
    do! set_render_text strings.Paper (Vector2(50.0f, 87.5f)) Color.White (Vector2(80.0f, 25.0f))
    do! use_state (fun s -> unfold{ s.IsZoomed <- true })
    do! zoom_in_to_enemie paper_ship_index 2.0f     
    
    do! set_render_text strings.Humid (Vector2(50.0f, 87.5f)) Color.White (Vector2(80.0f, 25.0f))
    do! zoom_in_to_enemie meat_ship_index 2.0f     

    do! set_render_text strings.Plastic (Vector2(50.0f, 87.5f)) Color.White (Vector2(80.0f, 25.0f))
    do! zoom_in_to_enemie plastic_ship_index 2.0f     
    do! remove_render_text
    

    do! use_state (fun s -> unfold{ s.IsZoomed <- false })
    do! zoom_out 2.0f
    
    do! set_render_text strings.Goal (Vector2(50.0f, 87.5f)) Color.White (Vector2(80.0f, 25.0f))
    do! parallel_
          (set_render_texts_with_tap [strings.Help1;
                                      strings.Help2 ] 
                                      (Vector2(50.0f, 87.5f)) Color.White (Vector2(80.0f, 25.0f)))     
          (unfold{
              do! set_render_texture_1 "UIWAG/circleTUTORIAL" (Vector2(51.0f, 53.0f)) Color.Green (Vector2(18.0f, 29.0f))
              do! set_render_texture_2 "UIWAG/circle_glow" (Vector2(93.0f, 20.0f)) Color.Green (Vector2(42.0f, 70.0f))
              do! wait 3.0f
              do! set_render_texture_1 "UIWAG/circleTUTORIAL" (Vector2(29.0f, 50.0f)) Color.Blue (Vector2(18.0f, 29.0f))
              do! set_render_texture_2 "UIWAG/circle_glow" (Vector2(7.0f, 20.0f)) Color.Blue (Vector2(42.0f, 70.0f))
              do! wait 3.0f
              do! set_render_texture_1 "UIWAG/circleTUTORIAL" (Vector2(72.0f, 50.0f)) Color.Yellow (Vector2(18.0f, 29.0f))
              do! set_render_texture_2 "UIWAG/circle_glow" (Vector2(7.0f, 80.0f)) Color.Yellow (Vector2(42.0f, 70.0f))
              do! wait 3.0f
            }) |> ignore_
    
    do! remove_render_texture_1
    do! remove_render_texture_2
    do! remove_render_text
    do! remove_top_render_text

    do! wait 3.0f

    do! set_render_texts_with_tap [strings.Do1;strings.Do2] (Vector2(50.0f, 87.5f)) Color.White (Vector2(80.0f, 25.0f))    
    do! remove_render_text
    do! remove_top_render_text
    do! set_render_texture_1 "TUTORIAL/Tutorial9" (Vector2(50.0f, 20.0f)) Color.White (Vector2(20.0f, 20.0f))

    do! use_garbage meat_ship_index (fun g -> unfold { g.set_touch(true) } )
    do! use_garbage plastic_ship_index (fun g -> unfold { g.set_touch(true) } )
    do! use_garbage paper_ship_index (fun g -> unfold { g.set_touch(true) } )
    do! parallel_first_
          (wait_while_ (unfold {  let! enemies_defeat = all_enemies_defeated
                                  return not enemies_defeat }))
          (parallel_many_
              [check_if_is_one_blue_collide_with_the_wrong_trash_bin strings;
               check_if_is_one_green_collide_with_the_wrong_trash_bin strings;
               check_if_is_one_yellow_collide_with_the_wrong_trash_bin strings]) |> ignore_

    do! remove_render_texture_1
    do! wait 2.0f
    do! set_render_texts_with_tap [strings.Do3] (Vector2(50.0f, 87.5f)) Color.White (Vector2(80.0f, 25.0f))
    do! remove_render_text
    do! remove_top_render_text

    let plastic_ship = new ShipToAdd(Vector3(-5.0f, -0.2f, 0.0f),Vector3(0.0f, -0.2f, 0.0f),true, 0.0f,GarbageType.EnemyGreenMeat,true,true)    
    do! add_enemie plastic_ship
    let paper_ship = new ShipToAdd(Vector3(5.0f, -0.2f, 0.0f),Vector3(0.0f, -0.2f, 0.0f),true, 0.0f,GarbageType.EnemyGreenSandwich,true,true)
    do! add_enemie paper_ship

    do! parallel_first_
          (wait_while_ (unfold {  let! enemies_defeat = all_enemies_defeated
                                  return not enemies_defeat }))
          (check_if_is_one_green_collide_with_the_wrong_trash_bin strings) 
          |> ignore_

    do! set_render_texts_with_tap [strings.Good] (Vector2(50.0f, 87.5f)) Color.White (Vector2(80.0f, 25.0f))
    do! remove_render_text
    do! remove_top_render_text

    do! parallel_
          (set_render_texts_with_tap  [strings.Help3;
                                       strings.Help4;
                                       strings.Help5] (Vector2(50.0f, 87.5f)) Color.White (Vector2(80.0f, 25.0f)))
          (unfold{
            do! wait 3.5f
            do audioManager.OnTrashMaster(4.f)
            do! set_render_texture_2 "UIWAG/circle_glow" (Vector2(93.0f, 20.0f)) Color.Green (Vector2(42.0f, 70.0f))
            do! use_state (fun state -> unfold { 
                                          state.num_of_bubble_wave <- state.num_of_bubble_wave + 1
                                          state.green_trash_unloading_animation <- State.TRASH_UNLOADING_ANIMATION_TIME
                                          state.GreenTrashBinState <- 0 })
                          }) |> ignore_

    
    do! set_render_texture_2 "UIWAG/rectangle_glow" (Vector2(1.0f, 54.0f)) Color.GreenYellow (Vector2(18.0f, 50.0f))
    do! set_render_texts_with_tap  [strings.Help6;
                                    strings.Help7] (Vector2(50.0f, 87.5f)) Color.White (Vector2(80.0f, 25.0f))
    do! remove_render_texture_2

    do! set_render_texts [strings.Good] 2.0f (Vector2(50.0f, 87.5f)) Color.White (Vector2(80.0f, 25.0f))


    do! set_render_texture_2 "UIWAG/rectangle_glow" (Vector2(68.0f, 3.0f)) Color.HotPink (Vector2(17.0f, 38.0f))
    do! (set_render_texts_with_tap  [strings.Help8;
                                     strings.Help9] (Vector2(50.0f, 87.5f)) Color.White (Vector2(80.0f, 25.0f)))
    do! remove_render_text
    do! remove_top_render_text
    do! generate_enemies 10

    do! wait_while_ (unfold {  let! enemies_defeat = all_enemies_defeated
                               return not enemies_defeat })
    do! remove_render_texture_2

    do! set_render_texts [strings.Good] 2.0f (Vector2(50.0f, 87.5f)) Color.White (Vector2(80.0f, 25.0f))

    do! set_render_texture_2 "UIWAG/rectangle_glow" (Vector2(34.0f, 3.0f)) Color.Red (Vector2(75.0f, 40.0f)) 
    do! set_render_texts_with_tap [strings.Help10;
                                   strings.Help11] (Vector2(50.0f, 87.5f)) Color.White (Vector2(80.0f, 25.0f))
    do! remove_render_text
    do! remove_top_render_text

    let meat_ship = new ShipToAdd(Vector3(0.0f, 0.0f, -6.8f),Vector3(12.5f,0.0f,8.0f),false, 2.0f,GarbageType.EnemyBlueBottle,true,true)
    do! add_enemie meat_ship
    do! wait_while_ (unfold { let! garbages = get_garbages
                              return not (garbages.Count = 0) }) 
    do! parallel_first_
          (manage_earth_life)
          (unfold{
              do! set_render_texts_with_tap [strings.Help12] (Vector2(50.0f, 87.5f)) Color.White (Vector2(80.0f, 25.0f))    
              do! wait 2.0f
              do! set_render_texts_with_tap [strings.Fine] (Vector2(50.0f, 87.5f)) Color.White (Vector2(80.0f, 25.0f)) 
          }) |> ignore_
  }
let tutorial (game:Game) (strings:TutorialStrings) : Script<GameResult> = 
  let audioManager = (game.Services.GetService(typeof<IAudioManager>)) :?> IAudioManager
  unfold{    
    do! set_onTap false
    do! set_render_texts_with_tap
          [strings.Intro1;
           strings.Intro2;
           strings.Intro3 ] (Vector2(50.0f, 87.5f)) Color.White (Vector2(80.0f, 25.0f))
    do! remove_render_text
    do! remove_top_render_text
    do! wait 2.0f
    do! parallel_first_
          (tutorial_logic audioManager strings)
          (check_defeat ()) |> ignore_
    return EntitiesAndGameState.GameState.Victory
  }